#include "list.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_WORD_LEN 24

//全局list_head表头
struct list_head g_head;
int g_count=0;

//统计项,包括单字字符串和出现次数
struct entry{
    char    word[MAX_WORD_LEN];
    int     count;
    struct list_head list;
};

const char*ignore_words[]={
    //冠词：如 
    "a", "an", "the",
    //介词：如 
    "in", "on", "at", "for", "of", "to","from","above","behind","by","with","about",
    //助动词：如 
    "is", "are", "have", "do","be","not","will","would", 
    "was" ,"has", "have", "had", "haven","could","can",
    //连词：如 
    "and", "but", "or","before","that","which","so",
    "then","because","if","also","when","where","how","what","as",
    //感叹词：如 
    "oh", "wow","well","hi",
    //代词
    "he","she","it","they","we","his","her","its","their","our","this","these","that","those",
    //限定词
    "any","some","all","none","no","one","each","every","which","many","much",
};


// 判断是否以特定后缀结尾
bool ends_with(const char* word, const char* suffix) {
    int word_len = strlen(word);
    int suffix_len = strlen(suffix);
    if (word_len < suffix_len) {
        return false;
    }
    return strncmp(word + word_len - suffix_len, suffix, suffix_len) == 0;
}

// 转换为小写
void to_lower_str(char* str) {
    for (int i = 0; str[i]; i++) {
        str[i] = tolower(str[i]);
    }
}

// 获取复数形式
void get_plural_form(const char* word, char* plural) {
    strcpy(plural, word);
    to_lower_str(plural);

    int len = strlen(plural);

    // 直接加s型
    if (ends_with(plural, "s") || ends_with(plural, "z") || ends_with(plural, "x") ||
        ends_with(plural, "sh") || ends_with(plural, "ch")) {
        plural[len] = 'e';
        plural[len + 1] = 's';
        plural[len + 2] = '\0';
    }
    // y尾型
    else if (ends_with(plural, "y")) {
        plural[len - 1] = 'i';
        plural[len] = 'e';
        plural[len + 1] = 's';
        plural[len + 2] = '\0';
    }
    // o尾型
    else if (ends_with(plural, "o")) {
        plural[len] = 'e';
        plural[len + 1] = 's';
        plural[len + 2] = '\0';
    }
    // f尾型
    else if (ends_with(plural, "f")) {
        plural[len - 1] = 'v';
        plural[len] = 'e';
        plural[len + 1] = 's';
        plural[len + 2] = '\0';
    } else if (ends_with(plural, "fe")) {
        plural[len - 2] = 'v';
        plural[len - 1] = 'e';
        plural[len] = 's';
        plural[len + 1] = '\0';
    }
    // 同形型和不规则型
    else {
        plural[len] = 's';
        plural[len + 1] = '\0';
    }
}


/*
比较两个单词是否相同,同一单词的单复数变化视作相同
1、直接加s型。

2、s尾型：以s，z，x，sh，ch结尾的名词。

3、y尾型：以y结尾的名词。

4、o尾型：以o结尾的名词。

5、f尾型：以f或fe结尾的词。

6、同形型：有少数单词单复数同形。

7、不规则型：有一部分单词的复数是不规则变化。
*/
bool is_same_word(const char* word1,const char* word2){
    char plural1[MAX_WORD_LEN] = {0};
    char plural2[MAX_WORD_LEN] = {0};

    // 转换为小写
    char lower_word1[MAX_WORD_LEN];
    char lower_word2[MAX_WORD_LEN];
    strcpy(lower_word1, word1);
    strcpy(lower_word2, word2);
    to_lower_str(lower_word1);
    to_lower_str(lower_word2);

    // 如果两个单词相同，直接返回true
    if (strcasecmp(word1, word2) == 0) {
        return true;
    }

    // 获取两个单词的复数形式
    get_plural_form(lower_word1, plural1);
    get_plural_form(lower_word2, plural2);

    // 比较单词和对方的复数形式
    if (strcasecmp(lower_word1, plural2) == 0 || strcasecmp(lower_word2, plural1) == 0) {
        return true;
    }

    return false;

}

//将单词添加到表中
void add_word(const char* word){
    //检查是否有重复的，增加计数
    struct list_head *p;
    struct entry *e;
    bool found = false;

    list_for_each_entry(e, &g_head, list)
    {
        if(is_same_word(e->word,word)){
            found = true;
            break;
        }
    }

    if(!found){
        e = (struct entry*)malloc(sizeof(struct entry));
        strcpy(e->word,word);
        e->count = 1;
        list_add(&e->list, &g_head);
    }else{
        e->count++;
    }
}

//从文件读取字符,建立统计表
//todo 忽略虚词，处理同一个词的不同时态和单复数形式
void build_list(FILE* fp){
    char c = 0;

    //初始化链表
    INIT_LIST_HEAD(&g_head);  

    //遍历文件内容,存入链表
    while(EOF != (c = getc(fp))){
        bool skip = false;
        if(isalpha(c)){
            char word[MAX_WORD_LEN]={0};
            int i=0;
            do{
                word[i++]=c;
            }while(isalpha((c=getc(fp))));
            //忽略虚词
            for(int i=0;i<sizeof(ignore_words)/sizeof(char*);i++){
                if(is_same_word(ignore_words[i],word)){
                    skip = true;
                    break;
                }
            }
            if(!skip && strlen(word)>1){
                add_word(word);
            }
            // g_count++;
        }
    }
}

//插入排序,按照从高到低顺序
void insert_sort(struct list_head* head){
    //建一个新的链表
    struct list_head new_head;
    INIT_LIST_HEAD(&new_head);

    //从原有的链表里逐个删除节点,并插入到新链表里
    struct entry* e = NULL,*tmp=NULL;
    //使用safe,因为在遍历的过程中move会改变链接关系
    list_for_each_entry_safe(e,tmp,head,list){
        struct entry* new_first = list_entry(new_head.next, struct entry, list);
        //新表为空,或当前遍历到的元素大于等于新表第一个元素,则插入到新表的头部
        if(list_empty(&new_head) || e->count >= new_first->count ){
            list_move(&e->list,&new_head);
        }else{
            struct entry*pos=NULL;
            //在新表里寻找第一个可以插入的位置
            list_for_each_entry(pos, &new_head, list){
                if(pos->count < e->count){
                    break;
                }
            }
            //从旧表删除元素,并插入新表
            list_del(&e->list);
            e->list.prev = pos->list.prev;
            e->list.next = &pos->list;
            pos->list.prev->next = &e->list;
            pos->list.prev = &e->list;
        }
    }

    //最后再替换成原有的链表头
    head->prev = new_head.prev;
    new_head.prev->next=head;
    head->next = new_head.next;
    new_head.next->prev=&new_head;
}

// 打印结果，默认打印前50个
void print_result(int n) {
    struct list_head *p;
    struct entry *e;
    int i = 0;

    // 打印标题
    printf("%-30s %-10s %-10s\n", "Word", "Count", "Bar Chart");
    printf("%-30s %-10s %-10s\n", "----", "-----", "---------");

    list_for_each_entry(e, &g_head, list) {
        // 打印单词和计数
        printf("%-30s %-10d ", e->word, e->count);

        // 打印条形图
        for (int j = 0; j < e->count; j++) {
            printf("*");
        }
        printf("\n");

        i++;
        if (i >= n) {
            break;
        }
    }
}

int main(int argc, char* argv[]){
    FILE* fp = NULL;

    //从参数读取文件名
    if(argc==2){
        //打开文件
        fp = fopen(argv[1],"r");//r for read-only
        if(!fp){
            printf("can not open %s\n",argv[1]);
            return -1;
        }
    }else{
        printf("invalid argument number, usage: ./a.out [filepath]\n");
        return -1;
    }
    

    //建表
    build_list(fp);

    //排序
    insert_sort(&g_head);

    //打印
    print_result(50);

    // printf("total count=%d\n",g_count);

    //关闭文件
    fclose(fp);

    return 0;
}